package com.cn.tous.auth.security;

import cn.hutool.core.lang.Assert;
import com.cn.tous.auth.oauth2.token.OidcIdTokenGenerator;
import lombok.Setter;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.*;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;
import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.util.StringUtils;

import java.security.Principal;
import java.util.HashSet;
import java.util.Set;

/**
 * @author mengwei
 * @description
 *   自定义的密码模式认证提供者
 *
 * @createDate 2025/7/29 10:47
 */

@Setter
public class PasswordOAuth2AuthorizationGrantAuthenticationProvider implements AuthenticationProvider {

    private OAuth2TokenGenerator<?> tokenGenerator;

    private UserDetailsService userDetailsService;


    private PasswordEncoder passwordEncoder;

    private AuthorizationServerContext context;

    private OAuth2AuthorizationService authorizationService;

    public PasswordOAuth2AuthorizationGrantAuthenticationProvider(OAuth2TokenGenerator<?> tokenGenerator
            , UserDetailsService userDetailsService, PasswordEncoder passwordEncoder, AuthorizationServerContext context
            , OAuth2AuthorizationService authorizationService) {
        Assert.notNull(tokenGenerator, "tokenGenerator cannot be null");
        this.tokenGenerator = tokenGenerator;
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
        this.context = context;
        this.authorizationService = authorizationService;

    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        OAuth2PasswordGrantAuthenticationToken passwordAuthentication = (OAuth2PasswordGrantAuthenticationToken) authentication;

        OAuth2ClientAuthenticationToken clientPrincipal = (OAuth2ClientAuthenticationToken) passwordAuthentication.getPrincipal();
        RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();


        // 1. 验证客户端是否支持 password 授权类型
        if (!registeredClient.getAuthorizationGrantTypes().contains(new AuthorizationGrantType("password"))) {
            throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
        }

        // 2. 获取用户名和密码
        String username = passwordAuthentication.getUsername();
        String password = passwordAuthentication.getPassword();

        if (!StringUtils.hasText(username) || !StringUtils.hasText(password)) {
            throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST));
        }

        // 3. **关键：验证用户名和密码**
        // 这里需要调用 UserDetailsService 来验证凭据
        // 由于 UserDetailsService 通常在 AuthenticationManager 中使用，
        // 我们需要一种方式来注入和调用它。这里简化处理，假设有一个方法 validateUserCredentials(username, password)
        // 实际中，您可能需要将 UserDetailsService 注入到此 Provider 或通过其他方式验证。
        // 为了演示，我们假设验证通过，并获取 UserDetails (包含权限)
        UserDetails userDetails = validateUserCredentials(username, password); // 伪代码

        if (userDetails == null) {
            throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT, "Invalid username or password", null));
        }

        UsernamePasswordAuthenticationToken userPrincipal = new UsernamePasswordAuthenticationToken(userDetails
                , userDetails.getPassword(), userDetails.getAuthorities());

        // 4. 构建 OAuth2Authorization
        // 这个对象代表了用户对客户端的授权
        Set<String> authorizedScopes = new HashSet<>(passwordAuthentication.getScopes());
        // 安全起见，从客户端配置的 scope 中取交集，或根据用户权限动态决定
        // authorizedScopes.retainAll(registeredClient.getScopes()); // 可选：限制在客户端注册的 scope 内
        // 或者根据 userPrincipal 的权限决定 authorizedScopes

        OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)
                .principalName(userPrincipal.getName())
                .authorizationGrantType(new AuthorizationGrantType("password"))
                .authorizedScopes(authorizedScopes)
                // 存储用户主体
                .attribute(Principal.class.getName(), userPrincipal);

        // 5. 生成 Access Token
        DefaultOAuth2TokenContext.Builder contextBuilder = DefaultOAuth2TokenContext.builder()
                .registeredClient(registeredClient)
                // 传入用户主体
                .principal(userPrincipal)
                // TODO
//                .authorizationServerContext(AuthorizationServerContext.get())
                .authorizationServerContext(context)
                .authorizationGrantType(new AuthorizationGrantType("password"))
                .authorizationGrant(passwordAuthentication)
                .tokenType(OAuth2TokenType.ACCESS_TOKEN)
                .authorizedScopes(authorizedScopes);



        OAuth2Token accessToken = this.tokenGenerator.generate(contextBuilder.build());
        contextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN);

        OAuth2Token refreshToken = this.tokenGenerator.generate(contextBuilder.build());

        if (accessToken == null) {
            OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, "The token generator failed to generate the access token.", null);
            throw new OAuth2AuthenticationException(error);
        }
        if (refreshToken == null) {
            OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, "The token generator failed to generate the refresh token.", null);
            throw new OAuth2AuthenticationException(error);
        }

        OAuth2AccessToken auth2AccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, accessToken.getTokenValue(),  accessToken.getIssuedAt(), accessToken.getExpiresAt(), authorizedScopes);
        OAuth2RefreshToken auth2RefreshToken = new OAuth2RefreshToken(refreshToken.getTokenValue(), refreshToken.getIssuedAt(), refreshToken.getExpiresAt());

        // 6. 保存授权 (可选，如果需要持久化授权记录)
        // OAuth2AuthorizationService authorizationService = ...;
        // OAuth2Authorization authorization = authorizationBuilder.build();
        // authorizationService.save(authorization);

        // 7. 构建并返回成功认证的 Token
        OAuth2AccessTokenAuthenticationToken token = new OAuth2AccessTokenAuthenticationToken(
                registeredClient,
                clientPrincipal,
                auth2AccessToken,
                auth2RefreshToken
        );

        authorizationBuilder.accessToken(auth2AccessToken).refreshToken(auth2RefreshToken);

        // 8、生成 IdToken
//        registeredClient.getClientId();
//        clientPrincipal.getPrincipal();
//        context.getIssuer();
        OidcIdToken oidcIdToken =  OidcIdTokenGenerator.generateIdToken(clientPrincipal, registeredClient.getClientId(), "", auth2AccessToken, context.getIssuer());
        authorizationBuilder.token(oidcIdToken);

        //  9、token 与 授权信息 关联
        OAuth2Authorization authorization = authorizationBuilder.build();
        authorizationService.save(authorization);

        SecurityContextHolder.getContext().setAuthentication(token);
        return token;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return OAuth2PasswordGrantAuthenticationToken.class.isAssignableFrom(authentication);
    }

    // 伪方法：实际中应调用 UserDetailsService
    private UserDetails validateUserCredentials(String username, String password) {
        // 调用 UserDetailsService.loadUserByUsername(username)
        // 并验证密码 (使用 PasswordEncoder.matches(password, userDetails.getPassword()))
        // 返回 UserDetails 或 null
        // 这里简化返回一个 Principal
        // **在实际实现中，您需要正确注入并调用 UserDetailsService**
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        if (userDetails == null) {
            return null;
        }
        String password1 = userDetails.getPassword();
        if (!StringUtils.hasText(password1)) {
           return null;
        }
        return userDetails;

//        return new org.springframework.security.core.userdetails.User(username,
//                password, // 注意：实际不应存储明文密码
//                Collections.singletonList(new org.springframework.security.core.authority.SimpleGrantedAuthority("ROLE_USER")));
    }






}
